#ifndef FAKE_H
#define FAKE_H

#include "Action.h"
#include "Func.h"
#include "NotImplementedException.h"
#include "WhenCalled.h"
#include "WhenCalledReturn.h"

////////////////////////////////////////////////////////////////////////////////////////////////////

template<typename T>
class Fake
{
public:
    Fake();
    virtual ~Fake();

    T* operator ->();

    WhenCalled<T> whenCalled(Action0(func));

    template<typename TArg>
    WhenCalledArg<T, TArg> whenCalled(Action1(func));

    template<typename TArg1, typename TArg2>
    WhenCalledArgs2<T, TArg1, TArg2> whenCalled(Action2(func));

    template<typename TArg1, typename TArg2, typename TArg3>
    WhenCalledArgs3<T, TArg1, TArg2, TArg3> whenCalled(Action3(func));

    template<typename TArg1, typename TArg2, typename TArg3, typename TArg4>
    WhenCalledArgs4<T, TArg1, TArg2, TArg3, TArg4> whenCalled(Action4(func));

    template<typename TResult>
    WhenCalledReturn<T, TResult> whenCalled(Func0(func));

    template<typename TArg, typename TResult>
    WhenCalledReturnArg<T, TArg, TResult> whenCalled(Func1(func));

    template<typename TArg1, typename TArg2, typename TResult>
    WhenCalledReturnArgs2<T, TArg1, TArg2, TResult> whenCalled(Func2(func));

    template<typename TArg1, typename TArg2, typename TArg3, typename TResult>
    WhenCalledReturnArgs3<T, TArg1, TArg2, TArg3, TResult> whenCalled(Func3(func));

    template<typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TResult>
    WhenCalledReturnArgs4<T, TArg1, TArg2, TArg3, TArg4, TResult> whenCalled(Func4(func));
};

//--------------------------------------------------------------------------------------------------

template<typename T>
Fake<T>::Fake()
{
}

//--------------------------------------------------------------------------------------------------

template<typename T>
Fake<T>::~Fake()
{
}

//--------------------------------------------------------------------------------------------------

template<typename T>
T* Fake<T>::operator->()
{
    throw NotImplementedException();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
WhenCalled<T> Fake<T>::whenCalled(Action0(func))
{
    return WhenCalled<T>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg>
WhenCalledArg<T, TArg> Fake<T>::whenCalled(Action1(func))
{
    return WhenCalledArg<T, TArg>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg1, typename TArg2>
WhenCalledArgs2<T, TArg1, TArg2> Fake<T>::whenCalled(Action2(func))
{
    return WhenCalledArgs2<T, TArg1, TArg2>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg1, typename TArg2, typename TArg3>
WhenCalledArgs3<T, TArg1, TArg2, TArg3> Fake<T>::whenCalled(Action3(func))
{
    return WhenCalledArgs3<T, TArg1, TArg2, TArg3>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg1, typename TArg2, typename TArg3, typename TArg4>
WhenCalledArgs4<T, TArg1, TArg2, TArg3, TArg4> Fake<T>::whenCalled(Action4(func))
{
    return WhenCalledArgs4<T, TArg1, TArg2, TArg3, TArg4>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TResult>
WhenCalledReturn<T, TResult> Fake<T>::whenCalled(Func0(func))
{
    return WhenCalledReturn<T, TResult>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg, typename TResult>
WhenCalledReturnArg<T, TArg, TResult> Fake<T>::whenCalled(Func1(func))
{
    return WhenCalledReturnArg<T, TArg, TResult>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg1, typename TArg2, typename TResult>
WhenCalledReturnArgs2<T, TArg1, TArg2, TResult> Fake<T>::whenCalled(Func2(func))
{
    return WhenCalledReturnArgs2<T, TArg1, TArg2, TResult>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg1, typename TArg2, typename TArg3, typename TResult>
WhenCalledReturnArgs3<T, TArg1, TArg2, TArg3, TResult> Fake<T>::whenCalled(Func3(func))
{
    return WhenCalledReturnArgs3<T, TArg1, TArg2, TArg3, TResult>();
}

//--------------------------------------------------------------------------------------------------

template<typename T>
template<typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TResult>
WhenCalledReturnArgs4<T, TArg1, TArg2, TArg3, TArg4, TResult> Fake<T>::whenCalled(Func4(func))
{
    return WhenCalledReturnArgs4<T, TArg1, TArg2, TArg3, TArg4, TResult>();
}

////////////////////////////////////////////////////////////////////////////////////////////////////

#endif
